<?php
require_once GALAXYTOOL_ROOT."/secret/includes/db_handler.inc.php";
require_once GALAXYTOOL_ROOT."/secret/includes/error_object.inc.php";

/**
 * PHP parser for the OGame API data provided with differnt XML files
 *
  * @author eX0du5
 *
 */
class OGameAPIParser extends GenericSuperclass {

	const category_player = 1;
	const category_alliance = 2;

	const type_total              = 0;
	const type_Economy            = 1;
	const type_Research           = 2;
	const type_Military           = 3;
	const type_Military_Lost      = 4;
	const type_Military_Built     = 5;
	const type_Military_Destroyed = 6;
	const type_Honor              = 7;

	private $dbtablename = null;
	private $systemtablename = null;
	private $allytable = null;
	private $playertable = null;
	private $global_universe = null;
	private $depth = 0;

	public function __construct($dbcon, $global_universe, $dbtablename, $systemtablename, $playertable, $allytable) {
		parent::__construct($dbcon);

		if ($dbtablename == "") throw new InvalidArgumentException("Galaxy table empty");
		$this->dbtablename = $dbtablename;

		if ($systemtablename == "") throw new InvalidArgumentException("Galaxy table empty");
		$this->systemtablename = $systemtablename;

		if ($playertable == "") throw new InvalidArgumentException("Player table empty");
		$this->playertable = $playertable;

		if ($allytable == "") throw new InvalidArgumentException("Ally table empty");
		$this->allytable = $allytable;

		if ($global_universe == "") throw new InvalidArgumentException("Universe empty");
		$this->global_universe = $global_universe;
	}

	/**
	 * Load universe and statistics data from OGame API provided at attributes.php and update tool information.
	 * @param Integer $user_id
	 * @throws InvalidArgumentException
	 * @return boolean
	 */
	public function load_OGame_data($user_id) {
		if (!defined("OGAME_URL")) throw new InvalidArgumentException("OGame URL not defined");
		$user_id = intval($user_id);
		if ($user_id < 1) throw new InvalidArgumentException("Invalid user id provided");

		$result = $this->load_OGame_universe_data($user_id);
		if ($result === true) {
			$result = $this->load_OGame_statistics_data($user_id);
		}

		return $result;
	}

	/**
	 * Load universe data from OGame API provided at attributes.php and update tool information.
	 * @param Integer $user_id
	 * @throws InvalidArgumentException
	 * @return boolean
	 */
	public function load_OGame_universe_data($user_id) {
		if (!defined("OGAME_URL")) throw new InvalidArgumentException("OGame URL not defined");
		$user_id = intval($user_id);
		if ($user_id < 1) throw new InvalidArgumentException("Invalid user id provided");

		try {
			// universe data
			$result = $this->refresh_universe_data(OGAME_URL."/api/universe.xml",$user_id);
			if ($result === false) {
				return false;
			}
			unset($result);

		} catch (Exception $e) {
			$this->error_object = new ErrorObject(ErrorObject::severity_error, $e->getMessage() );
			return false;
		}
		return true;
	}

	/**
	 * Load statistics data from OGame API provided at attributes.php and update tool information.
	 * @param Integer $user_id
	 * @throws InvalidArgumentException
	 * @return boolean
	 */
	public function load_OGame_statistics_data($user_id) {
		if (!defined("OGAME_URL")) throw new InvalidArgumentException("OGame URL not defined");
		$user_id = intval($user_id);
		if ($user_id < 1) throw new InvalidArgumentException("Invalid user id provided");

		try {
			// alliance data
			$result = $this->get_alliance_data(OGAME_URL."/api/alliances.xml");

			if ($result === false || $result == null) {
				return false;
			}
			for ($i=0;$i<=7;$i++) {
				$result = $this->enrich_data($result,OGAME_URL."/api/highscore.xml?category=2&type=$i");
				if ($result === false) {
					return false;
				}
			}
			$result2 = $this->save_allydata($result, $user_id);
			if ($result2 === false) {
				return false;
			}
			unset($result);
			unset($result2);

			// player data
			$result = $this->get_player_data(OGAME_URL."/api/players.xml");
			if ($result === false) {
				return false;
			}
			for ($i=0;$i<=7;$i++) {
				$result = $this->enrich_data($result,OGAME_URL."/api/highscore.xml?category=1&type=$i");
				if ($result === false) {
					return false;
				}
			}
			$result2 = $this->save_playerdata($result, $user_id);
			if ($result2 === false) {
				return false;
			}

		} catch (Exception $e) {
			$this->error_object = new ErrorObject(ErrorObject::severity_error, $e->getMessage() );
			return false;
		}

		return true;
	}


	/**
	 * Load universe and statistics data from OGame API provided at $url and update tool information.
	 * @param String $url
	 * @param Integer $user_id
	 * @throws InvalidArgumentException
	 * @return void|boolean
	 */
	protected function refresh_universe_data($url,$user_id) {
		$user_id = intval($user_id);
		if ($user_id < 1) throw new InvalidArgumentException("Invalid user id provided");
		$url = strip_tags($url);
		if (substr($url, -4) != ".xml") throw new InvalidArgumentException("No XML file provided");

		if (!$this->validate_url($url)) return false;

		$planet    = 0;
		$last_update = null;
		$query = "INSERT INTO $this->dbtablename (galaxy, system, planet, ogame_playerid, planetname, moon, moonsize) VALUES ";
		$systemquery = "INSERT INTO $this->systemtablename (galaxy, system, last_update, user_id) VALUES ";
		$query_content = "";
		$system_query_content = "";

		$xml = new XMLReader();
		$xml->open($url);
		$xml->read();
		if ($xml->name != "universe") {
			$this->error_object = new ErrorObject(ErrorObject::severity_error, "Unexpected Error: Invalid XML content at $url - no universe tag found as first tag");
			return;
		}
		$attributes = array();
	    while($xml->moveToNextAttribute()) {
	    	$attributes[$xml->name] = $xml->value;
	    }
	    if (!array_key_exists("timestamp", $attributes)) {
	    	$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - no timestamp attribute found on universe tag");
	    	return;
	    }
		$last_update = intval(strip_tags($attributes['timestamp']));
	    $last_update = date("Y-m-d H:i:s",$last_update);

	    /*
	     * clean old data:
	     * - remove all entries where no debris field exists
	     * - clear all attributes on remaining entries except the debris values
	     */
	    $cleanup_query = "DELETE FROM $this->dbtablename WHERE metal = 0 AND crystal = 0";
	    $res = $this->run_mysql_query($cleanup_query);
		if (!$res) return false;
		unset($res, $cleanup_query);
	    $cleanup_query = "UPDATE $this->dbtablename set ogame_playerid=0,moon='false', moonsize=0, planetname=''";
	    $res = $this->run_mysql_query($cleanup_query);
		if (!$res) return false;
		unset($res, $cleanup_query);

		while($xml->read()) {
			if($xml->nodeType == XMLReader::ELEMENT) {
				$current_tag = $xml->name;
				if ($xml->name != "planet") {
					// unknown tag - ignore it
					continue;
				}

				// check if we have a moon tag inside the current planet tag
				if(!$xml->isEmptyElement && $current_tag == "planet") {
					$has_moon = true;
	    		} elseif($current_tag == "planet") {
	    			$has_moon = false;
	    		}

	    		// now copy the planet attributes
				$node = array();
				if($xml->hasAttributes) {
					unset($attributes);
	                $attributes = array();
	                while($xml->moveToNextAttribute()) {
	                    $attributes[$xml->name] = $xml->value;
	                }
	                $node['planet'] = $attributes;
	            }

	            // now read the moon data if available
	            if ($has_moon) {
	            	$xml->read(); // get moon tag
	            	// copy moon attributes
					if($xml->hasAttributes) {
						unset($attributes);
		                $attributes = array();
		                while($xml->moveToNextAttribute()) {
		                    $attributes[$xml->name] = $xml->value;
		                }
		                $node['moon'] = $attributes;
		            }
	            }

				$planet++;

				if (!array_key_exists("planet", $node)) {
    				$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - no planet tag");
    				return false;
    			}

				// moon and moon size
				$moon = (array_key_exists("moon", $node) === true) ? "true" : "false";
    			if ($moon == "true") {
    				if (!array_key_exists("size", $node['moon'])) {
    					$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - no size at moon tag");
    					return false;
    				}
    				$moonsize = intval($node['moon']['size']);
    			} else {
    				$moonsize = 0;
    			}

    			// other attributes
    			if (!array_key_exists("coords", $node['planet'])) {
    				$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - no coords at planet tag");
    				return false;
    			}
    			if (!array_key_exists("name", $node['planet'])) {
    				$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - no name at planet tag");
    				return false;
    			}
    			if (!array_key_exists("player", $node['planet'])) {
    				$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - no player at planet tag");
    				return false;
    			}

    			$coords   = explode(":", $node['planet']['coords']);
    			$name     = mysql_real_escape_string($node['planet']['name']);
    			$playerid = intval($node['planet']['player']);

    			$query_content        .= "('".intval($coords[0])."','".intval($coords[1])."','".intval($coords[2])."','".$playerid."','".$name."','$moon','$moonsize'),";
	    		$system_query_content .= "('".intval($coords[0])."','".intval($coords[1])."','$last_update','$user_id'),";

				unset($node);

			    // paging required!!!
    			if ($planet % 1000 == 0) {
    				$query_content = substr($query_content, 0,strlen($query_content)-1); // remove comma
			   		$query_content .= " ON DUPLICATE KEY UPDATE planetname=VALUES(planetname), ogame_playerid=VALUES(ogame_playerid), moon=VALUES(moon), moonsize=VALUES(moonsize)";
			   		$res = $this->run_mysql_query($query . $query_content);
			   		if (!$res) return false;
			   		unset($res);
			   		$query_content = "";

			   		$system_query_content = substr($system_query_content, 0,strlen($system_query_content)-1); // remove comma
			   		$system_query_content .= " ON DUPLICATE KEY UPDATE last_update=VALUES(last_update), user_id=VALUES(user_id)";
			   		$res = $this->run_mysql_query($systemquery. $system_query_content);
			   		if (!$res) return false;
			   		unset($res);
			   		$system_query_content = "";
    			}
			}
		}
	    if ($query_content != "") {
    		$query_content = substr($query_content, 0,strlen($query_content)-1); // remove comma
    		$query_content .= " ON DUPLICATE KEY UPDATE planetname=VALUES(planetname), ogame_playerid=VALUES(ogame_playerid), moon=VALUES(moon), moonsize=VALUES(moonsize)";
    		$res = $this->run_mysql_query($query . $query_content);
    		if (!$res) return false;
    		unset($query_content);
   		}
    	if ($system_query_content != "") {
    		$system_query_content = substr($system_query_content, 0,strlen($system_query_content)-1); // remove comma
			$system_query_content .= " ON DUPLICATE KEY UPDATE last_update=VALUES(last_update), user_id=VALUES(user_id)";
			$res = $this->run_mysql_query($systemquery. $system_query_content);
    		if (!$res) return false;
    		unset($systemquery);
    	}
		$xml->close();

		/*
		 * Update all systems with current timestamp where no planet exists
		 */
		$query = "UPDATE $this->systemtablename SET last_update='$last_update' WHERE last_update != '$last_update'";
		$res = $this->run_mysql_query($query);
    	if (!$res) return false;

		return true;

	}

	/**
	 * Load statistics data from OGame API provided at $url and update tool information.
	 * @param String $url
	 * @throws InvalidArgumentException
	 * @return array|boolean
	 */
	protected function get_alliance_data($url) {
		$url = strip_tags($url);
		if (substr($url, -4) != ".xml") throw new InvalidArgumentException("No XML file provided");

		if (!$this->validate_url($url)) return false;

		$xml = new XMLReader();
		$xml->open($url);
		$xml->read();

		if ($xml->name != "alliances") {
			$this->error_object = new ErrorObject(ErrorObject::severity_error, "Unexpected Error: Invalid XML content at $url - no alliances tag found as first tag");
			return;
		}
		$attributes = array();
	    while($xml->moveToNextAttribute()) {
	    	$attributes[$xml->name] = $xml->value;
	    }
	    if (!array_key_exists("timestamp", $attributes)) {
	    	$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - no timestamp attribute found on alliances tag");
	    	return;
	    }
		$last_update = intval(strip_tags($attributes['timestamp']));
	    $last_update = date("Y-m-d H:i:s",$last_update);

		$ally_buffer = array();

		$ally_buffer["last_update"] = $last_update;

		while($xml->read()) {
			if($xml->nodeType == XMLReader::ELEMENT) {
				$current_tag = $xml->name;
				if ($xml->name == "alliance") {

					if($xml->hasAttributes) {
						unset($attributes);
		                $attributes = array();
		                while($xml->moveToNextAttribute()) {
		                    $attributes[$xml->name] = $xml->value;
		                }
		            }

    				if (!array_key_exists("id", $attributes)) {
    					$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - no id attribute at alliance tag");
    					return false;
    				}
    				if (!array_key_exists("tag", $attributes)) {
    					$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - no tag attribute at alliance tag");
    					return false;
    				}


					$last_allyid = $attributes['id'];
					$ally_buffer[ $last_allyid ]['t'] = $attributes['tag']; // allyid -> allytag
	    			$ally_buffer[ $last_allyid ]['m'] = 0;
				} elseif($xml->name == "player") {
					$ally_buffer[ $last_allyid ]['m']++;
				} // else ignore unknown tag
			}
		} // while

	    return $ally_buffer;

	}

	/**
	 * Extract player information from OGame API and return as array
	 * @param String $url
	 * @throws InvalidArgumentException
	 * @return boolean|array
	 */
	protected function get_player_data($url) {
		$url = strip_tags($url);
		if (substr($url, -4) != ".xml") throw new InvalidArgumentException("No XML file provided");

		if (!$this->validate_url($url)) return false;

		$xml = new XMLReader();
		$xml->open($url);
		$xml->read();
		if ($xml->name != "players") {
			$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - no players tag found as first tag");
			return;
		}
		$attributes = array();
	    while($xml->moveToNextAttribute()) {
	    	$attributes[$xml->name] = $xml->value;
	    }
	    if (!array_key_exists("timestamp", $attributes)) {
	    	$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - no timestamp attribute found on alliances tag");
	    	return;
	    }
		$last_update = intval(strip_tags($attributes['timestamp']));
	    $last_update = date("Y-m-d H:i:s",$last_update);

		$player_buffer = array();

		$player_buffer["last_update"] = $last_update;

		while($xml->read()) {
			if($xml->nodeType == XMLReader::ELEMENT) {
				$current_tag = $xml->name;
				if ($xml->name == "player") {

					if($xml->hasAttributes) {
						unset($attributes);
		                $attributes = array();
		                while($xml->moveToNextAttribute()) {
		                    $attributes[$xml->name] = $xml->value;
		                }
		            }

    				if (!array_key_exists("id", $attributes)) {
    					$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - no id attribute at player tag");
    					return false;
    				}
    				if (!array_key_exists("name", $attributes)) {
    					$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - no name attribute at player tag");
    					return false;
    				}

    				$id = intval($attributes['id']);
    				$player_buffer[$id] =  $attributes['name'].",";
    				$player_buffer[$id] .= (array_key_exists("status", $attributes)) ? $attributes['status']."," : ",";
    				$player_buffer[$id] .= (array_key_exists("alliance", $attributes)) ? intval($attributes['alliance'])."," : ",";

				} // else ignore unknown tag
			}
		} // while

	    return $player_buffer;
	}

	/**
	 * Load statistics information from OGame API and enrich provided player or alliance array with that data.
	 * @param array $data
	 * @param String $url
	 * @throws InvalidArgumentException
	 * @return boolean|array
	 */
	protected function enrich_data(array $data, $url) {
		if (count($data) < 2) throw new InvalidArgumentException("Invalid data provided");
		$url = strip_tags($url);
		if (strpos($url,".xml") === false) throw new InvalidArgumentException("No XML file provided");

		if (!$this->validate_url($url)) return false;

		$xml = new XMLReader();
		$xml->open($url);
		$xml->read();
		if ($xml->name != "highscore") {
			$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - no highscore tag found as first tag");
			return;
		}
		$attributes = array();
	    while($xml->moveToNextAttribute()) {
	    	$attributes[$xml->name] = $xml->value;
	    }
	    if (!array_key_exists("timestamp", $attributes)) {
	    	$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - no timestamp attribute found on highscore tag");
	    	return;
	    }
	    if (!array_key_exists("type", $attributes)) {
	    	$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - no type attribute found on highscore tag");
	    	return;
	    }
	    if (!array_key_exists("category", $attributes)) {
	    	$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - no category attribute found on highscore tag");
	    	return;
	    }
	    $last_update = intval(strip_tags($attributes['timestamp']));
	    $last_update = date("Y-m-d H:i:s",$last_update);

	    $type     = intval($attributes['type']);
	    $category = intval($attributes['category']);

	    if ($category != 1 && $category != 2) {
	    	$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - invalid category: $category found on highscore tag");
	    	return;
	    }
	    $expected_tag = ($category == "2") ? "alliance" : "player";

		$ally_buffer = array();

		$ally_buffer["last_update"] = $last_update;

		while($xml->read()) {
			if($xml->nodeType == XMLReader::ELEMENT) {
				$current_tag = $xml->name;
				if ($xml->name == $expected_tag) {

					if($xml->hasAttributes) {
						unset($attributes);
		                $attributes = array();
		                while($xml->moveToNextAttribute()) {
		                    $attributes[$xml->name] = $xml->value;
		                }
		            }

    				if (!array_key_exists("position", $attributes)) {
    					$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - no position attribute at $expected_tag tag");
    					return false;
    				}
    				if (!array_key_exists("id", $attributes)) {
    					$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - no id attribute at $expected_tag tag");
    					return false;
    				}
				    if (!array_key_exists("score", $attributes)) {
    					$this->error_object = new ErrorObject(ErrorObject::severity_error, "Error: Invalid XML content - no score attribute at $expected_tag tag");
    					return false;
    				}

					$id = intval($attributes['id']);
	    			if (array_key_exists($id, $data)) {
	    				if ($expected_tag == "alliance") {
	    					$data[$id][$type] = array( intval($attributes['position']), intval($attributes['score']) );
	    				} else {
	    					$data[$id] .= intval($attributes['position']).",".intval($attributes['score']).",";
	    					if ($type == "3" && $category == "1") {
		    					if (array_key_exists("ships", $attributes)) {
		    						$data[$id] .= intval($attributes['ships']).",";
		    					} else {
		    						$data[$id] .= ",";
		    					}
	    					}

	    				}
	    			}

				} // else ignore unknown tag
			}
		} // while

	    return $data;

	}

	/**
	 * Convert alliance information array into database query statements and save them.
	 * @param array $data
	 * @param Integer $user_id
	 * @throws InvalidArgumentException
	 * @return boolean
	 */
	protected function save_allydata(array $data, $user_id) {
		$user_id = intval($user_id);
		if ($user_id < 1) throw new InvalidArgumentException("Invalid user id provided");

		if (count($data) < 2) return false;

		if (!array_key_exists("last_update", $data)) throw new InvalidArgumentException("Invalid data array provided");
		$last_udpate = $data['last_update'];
		unset($data['last_update']);

		$query_start = "INSERT INTO $this->allytable (ogame_allyid, allyname, rank, points, frank, fpoints, rrank, rpoints, erank, epoints, hrank, hpoints,".
			           "fbrank,fbpoints,fdrank,fdpoints,flrank,flpoints,members,last_update,user_id) VALUES ";
		$query_end = "ON DUPLICATE KEY UPDATE allyname=VALUES(allyname), members=VALUES(members), last_update=VALUES(last_update), user_id=VALUES(user_id),".
		             "rank=VALUES(rank), points=VALUES(points), frank=VALUES(frank), fpoints=VALUES(fpoints), rrank=VALUES(rrank), rpoints=VALUES(rpoints), erank=VALUES(erank), epoints=VALUES(epoints),".
		             "hrank=VALUES(hrank), hpoints=VALUES(hpoints), fbrank=VALUES(fbrank), fbpoints=VALUES(fbpoints), fdrank=VALUES(fdrank), fdpoints=VALUES(fdpoints), flrank=VALUES(flrank), flpoints=VALUES(flpoints)";

		$query = "";
		$entries = 0;
		foreach($data as $allyid => $allydata) {
			$entries++;
			$allytag = $allydata['t'];
			$member  = $allydata['m'];

			if (isset($allydata[OGameAPIParser::type_total])) {
				$rank    = $allydata[OGameAPIParser::type_total][0];
				$points  = $allydata[OGameAPIParser::type_total][1];
				$frank   = $allydata[OGameAPIParser::type_Military][0];
				$fpoints = $allydata[OGameAPIParser::type_Military][1];
				$rrank   = $allydata[OGameAPIParser::type_Research][0];
				$rpoints = $allydata[OGameAPIParser::type_Research][1];
				$erank   = $allydata[OGameAPIParser::type_Economy][0];
				$epoints = $allydata[OGameAPIParser::type_Economy][1];
				$hrank   = $allydata[OGameAPIParser::type_Honor][0];
				$hpoints = $allydata[OGameAPIParser::type_Honor][1];
				$fbrank   = $allydata[OGameAPIParser::type_Military_Built][0];
				$fbpoints = $allydata[OGameAPIParser::type_Military_Built][1];
				$fdrank   = $allydata[OGameAPIParser::type_Military_Destroyed][0];
				$fdpoints = $allydata[OGameAPIParser::type_Military_Destroyed][1];
				$flrank   = $allydata[OGameAPIParser::type_Military_Lost][0];
				$flpoints = $allydata[OGameAPIParser::type_Military_Lost][1];
			} else {
				$rank    = 0;
				$points  = 0;
				$frank   = 0;
				$fpoints = 0;
				$rrank   = 0;
				$rpoints = 0;
				$erank   = 0;
				$epoints = 0;
				$hrank   = 0;
				$hpoints = 0;
				$fbrank   = 0;
				$fbpoints = 0;
				$fdrank   = 0;
				$fdpoints = 0;
				$flrank   = 0;
				$flpoints = 0;
			}

			$query .= "(".intval($allyid).",'".mysql_real_escape_string($allytag)."',";
			$query .= $rank.",".$points.",".$frank.",".$fpoints.",".$rrank.",".$rpoints.",".$erank.",".$epoints.",".$hrank.",".$hpoints.",".
			          $fbrank.",".$fbpoints.",".$fdrank.",".$fdpoints.",".$flrank.",".$flpoints.",";
			$query .= intval($member).",'".mysql_real_escape_string($last_udpate)."',$user_id),";

			if ($entries == 500) {
				// remove last comma
				$query = substr($query, 0, strlen($query)-1);

				$execute_query = $query_start . " " . $query . " " . $query_end;
				$res = $this->run_mysql_query($execute_query);
				unset($execute_query);
				if (!$res) return false;

				$query = "";
				$entries = 0;
			}
		}
		if ($entries > 0) {
			// remove last comma
			$query = substr($query, 0, strlen($query)-1);

			$execute_query = $query_start . " " . $query . " " . $query_end;
			$res = $this->run_mysql_query($execute_query);
			unset($execute_query);
			if (!$res) return false;
		}

		return true;
	}

	/**
	 * Convert player data array into database queries and save them.
	 * @param array $data
	 * @param Integer $user_id
	 * @throws InvalidArgumentException
	 * @return boolean
	 */
	protected function save_playerdata(array $data, $user_id) {
		$user_id = intval($user_id);
		if ($user_id < 1) throw new InvalidArgumentException("Invalid user id provided");

		if (count($data) < 2) return false;

		if (!isset($data['last_update'])) throw new InvalidArgumentException("Invalid data array provided");
		$last_udpate = $data['last_update'];
		unset($data['last_update']);

		// get all alliances from database
		$query = "SELECT id, ogame_allyid FROM $this->allytable";
		$res = $this->run_mysql_query($query);
		if (!$res) return false;
		unset($query);
		$ogame_allyid_2_gt_id = array();
		while ($line = mysql_fetch_object($res)) {
			$ogame_allyid_2_gt_id[$line->ogame_allyid] = $line->id;
		}

		$query_start = "INSERT INTO $this->playertable (playername,ogame_playerid,alliance_id,ships,rank,points,frank,fpoints,rrank,rpoints,erank,epoints,hrank,hpoints,".
		               "fbrank,fbpoints,fdrank,fdpoints,flrank,flpoints,noob,banned,vacation,inactive,long_inactive,outlaw,last_stats_update,user_id) VALUES ";
		$query_end = "ON DUPLICATE KEY UPDATE playername=VALUES(playername), alliance_id=VALUES(alliance_id), ships=VALUES(ships), last_stats_update=VALUES(last_stats_update), user_id=VALUES(user_id),".
		             "rank=VALUES(rank), points=VALUES(points), frank=VALUES(frank), fpoints=VALUES(fpoints), rrank=VALUES(rrank), rpoints=VALUES(rpoints), erank=VALUES(erank), epoints=VALUES(epoints),".
		             "hrank=VALUES(hrank), hpoints=VALUES(hpoints), fbrank=VALUES(fbrank), fbpoints=VALUES(fbpoints), fdrank=VALUES(fdrank), fdpoints=VALUES(fdpoints), flrank=VALUES(flrank), flpoints=VALUES(flpoints),".
					 "noob=VALUES(noob),banned=VALUES(banned),vacation=VALUES(vacation),inactive=VALUES(inactive),long_inactive=VALUES(long_inactive),outlaw=VALUES(outlaw)";

		$query = "";
		$entries = 0;
		foreach($data as $playerid => $playerdata) {
			$entries++;
			$playerdata = explode(",", $playerdata);
			$name     = $playerdata[0];
			$status   = $playerdata[1];
			$alliance = (isset($playerdata[2]) && is_numeric($playerdata[2]) && array_key_exists($playerdata[2], $ogame_allyid_2_gt_id)) ? $ogame_allyid_2_gt_id[ $playerdata[2] ] : 0; // 0 as default - but not NULL

			$noob = $banned = $vacation = $inactive = $long_inactive = $outlaw = "false";
			if (!empty($status)) {
				if (strpos($status,"n") !== false) $noob = 'true' ;
				if (strpos($status,"b") !== false) $banned = 'true' ;
				if (strpos($status,"v") !== false) $vacation = 'true' ;
				if (strpos($status,"i") !== false) $inactive = 'true' ;
				if (strpos($status,"I") !== false) $long_inactive = 'true' ;
				if (strpos($status,"o") !== false) $outlaw = 'true' ;
			}
			if ($long_inactive == "true") $inactive = "true";


			if (isset($playerdata[4])) {
				$rank    = intval($playerdata[3]);
				$points  = intval($playerdata[4]);
				$erank   = intval($playerdata[5]);
				$epoints = intval($playerdata[6]);
				$rrank   = intval($playerdata[7]);
				$rpoints = intval($playerdata[8]);
				$frank   = intval($playerdata[9]);
				$fpoints = intval($playerdata[10]);
				$ships    = intval($playerdata[11]); // ships !!!
				$flrank   = intval($playerdata[12]);
				$flpoints = intval($playerdata[13]);
				$fbrank   = intval($playerdata[14]);
				$fbpoints = intval($playerdata[15]);
				$fdrank   = intval($playerdata[16]);
				$fdpoints = intval($playerdata[17]);
				$hrank   = intval($playerdata[18]);
				$hpoints = intval($playerdata[19]);
			} else {
				$rank    = 0;
				$points  = 0;
				$frank   = 0;
				$fpoints = 0;
				$ships   = 0;
				$rrank   = 0;
				$rpoints = 0;
				$erank   = 0;
				$epoints = 0;
				$hrank   = 0;
				$hpoints = 0;
				$fbrank   = 0;
				$fbpoints = 0;
				$fdrank   = 0;
				$fdpoints = 0;
				$flrank   = 0;
				$flpoints = 0;
			}

			$query .= "('".mysql_real_escape_string($name)."',".intval($playerid).",".$alliance.",".$ships.",";
			$query .= $rank.",".$points.",".$frank.",".$fpoints.",".$rrank.",".$rpoints.",".$erank.",".$epoints.",".$hrank.",".$hpoints.",".
			          $fbrank.",".$fbpoints.",".$fdrank.",".$fdpoints.",".$flrank.",".$flpoints.",".
			          "'$noob','$banned','$vacation','$inactive','$long_inactive','$outlaw',";
			$query .= "'".mysql_real_escape_string($last_udpate)."',$user_id),";

			if ($entries == 500) {
				// remove last comma
				$query = substr($query, 0, strlen($query)-1);

				$execute_query = $query_start . " " . $query . " " . $query_end;
				$res = $this->run_mysql_query($execute_query);
				unset($execute_query);
				if (!$res) return false;

				$query = "";
				$entries = 0;
			}
		}
		if ($entries > 0) {
			// remove last comma
			$query = substr($query, 0, strlen($query)-1);

			$execute_query = $query_start . " " . $query . " " . $query_end;
			$res = $this->run_mysql_query($execute_query);
			unset($execute_query);
			if (!$res) return false;
		}

		return true;
	}

	/**
	 * Checks if a given OGame API url exists at host.
	 * @param String $url OGame API URL including .xml
	 * @return boolean
	 */
	private function validate_url($url) {
		$one_byte_content = @file_get_contents($url,0,NULL,0,1);
		if ($one_byte_content === false) {
			$this->error_object = new ErrorObject(ErrorObject::severity_error, str_replace("&1",$url, INSTALL_STEP4_URL_CHECK_FAILED ));
			return false;
		}

		return true;
	}

}